package beans;

import exception.ExistException;
import entity.PerformanceItem;
import entity.Showing;
import entity.Area;
import entity.StakeholderTier;
import entity.StakeholderPerformance;
import entity.Venue;
import exception.OccupiedException;
import exception.OutstandingEventsException;
import exception.ShowNotFoundException;
import exception.TimingException;
import exception.VenueConflictException;
import helper.CalendarComparator;
import helper.PerformanceHelper;
import helper.Seats;
import helper.ShowHelper;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import javax.ejb.Stateful;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import java.util.Calendar;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import javax.persistence.Query;

/**
 * @Written by T12 (IS3102 AY2011/12)
 */
@Stateful
public class PerformanceTicketingSB implements PerformanceTicketingSBRemote {
    @PersistenceContext()
    private EntityManager em;
    private StakeholderPerformance stakeholder;
    private PerformanceItem event;
    private Venue venue;
    private Showing showing;
    private Area area;
    private SimpleDateFormat dateTimeFormat = new SimpleDateFormat("dd/MM/yyyy HH:mm");
    private SimpleDateFormat dateFormat = new SimpleDateFormat("dd/MM/yyyy");
    private SimpleDateFormat dateDayFormat = new SimpleDateFormat("dd/MM/yyyy(EEEE) hh:mm aa");
    
    public PerformanceTicketingSB(){
        
    }
    
    // add a stakeholder (needs to be removed when internally-integrated)
    public void add(){
        StakeholderTier gold = new StakeholderTier();
        StakeholderTier silver = new StakeholderTier();
        StakeholderTier bronze = new StakeholderTier();
        gold.create("Gold", 5, 599.00, 100.00, 4.00);
        silver.create("Silver", 4, 499.00, 200.00, 5.00);
        bronze.create("Bronze", 3, 399.00, 300.00, 6.00);
        em.persist(gold);
        em.persist(silver);
        em.persist(bronze);
        StakeholderPerformance stakeholder1 = new StakeholderPerformance();
        StakeholderPerformance stakeholder2 = new StakeholderPerformance();
        StakeholderPerformance stakeholder3 = new StakeholderPerformance();
        StakeholderPerformance stakeholder4 = new StakeholderPerformance();
        StakeholderPerformance stakeholder5 = new StakeholderPerformance();
        StakeholderPerformance stakeholder6 = new StakeholderPerformance();
        stakeholder1.create("Base Entertainment Asia", 0, silver, "admin@showbizasia.com");
        stakeholder2.create("Singapore Dance Theatre Limited", 0, gold, "sdtl@gmail.com");
        stakeholder3.create("JP Entertainment & Scorpio East Productions", 0, bronze, "admin@jp.com");
        stakeholder4.create("Resorts World at Sentosa Private Limited", 0, gold, "sentosa@rw.com");
        stakeholder5.create("Unusual Entertainment", 0, silver, "unusual@unusual.sg");
        stakeholder6.create("Mediacorp vizpro", 0, bronze, "vizpro@mediacorp.com.sg");
        em.persist(stakeholder1);
        em.persist(stakeholder2);
        em.persist(stakeholder3);
        em.persist(stakeholder4);
        em.persist(stakeholder5);
        em.persist(stakeholder6);
    }
    
    public List<String> getStakeholdersName(){
        List<String> output = new ArrayList<String>();
        Query q = em.createQuery("SELECT s FROM StakeholderPerformance s");
        for(Object o: q.getResultList()){
            StakeholderPerformance s = new StakeholderPerformance();
            s = (StakeholderPerformance)o;
            output.add(s.getName());        
        }
        return output;
    }
    
    public Long getStakeholderId(String stakeholderName){
        Long id = new Long(-1);
        Query q = em.createQuery("SELECT s FROM StakeholderPerformance s");
        for(Object o: q.getResultList()){
            StakeholderPerformance s = new StakeholderPerformance();
            s = (StakeholderPerformance)o; 
            if(s.getName().equalsIgnoreCase(stakeholderName)){
                id = new Long(s.getId());
            }
        }
        return id;
    }
    
    /*
     * Functionalities for TCube
     */
    /*
     * add a venue for events to be held
     */
    public void addVenue(String name, String address, String layout, Integer capacity, Double latitude, Double longtitude, boolean active) throws ExistException{
        venue = new Venue();
        Venue temp = new Venue();
        temp = em.find(Venue.class, name);
        if(temp == null){
            if (!(layout.matches("[\\w\\W]*.gif") || layout.matches("[\\w\\W]*.jpg")) || layout.matches("[\\w\\W]*.jpeg") || layout.matches("[\\w\\W]*.bmp") || layout.matches("[\\w\\W]*.png")) {
                layout = "picture/nolayout.gif";
            }
            venue.create(name, address, layout, capacity, latitude, longtitude, active);
            em.persist(venue);
            System.out.println("addVenue(): Venue " + name + " added.");
        }else{
            throw new ExistException("Venue " + name + " already exists.");
        }
    }
    
    /*
     * removes a venue for held events
     */
    public void removeVenue(String name) throws ExistException, OutstandingEventsException{
        venue = new Venue();
        venue = em.find(Venue.class, name);
        if(venue != null){
            boolean checkEmpty = true;
            Query q = em.createQuery("SELECT s FROM Showing s");
            for(Object o: q.getResultList()){
                showing = (Showing)o;
                if(showing.getVenue().getName().equalsIgnoreCase(name)){
                    checkEmpty = false;
                    break;
                }
            }
            if(checkEmpty){
                em.remove(venue);
                em.flush();
                System.out.println("removeVenue(): Venue " + name + " removed.");
            }else{
                throw new OutstandingEventsException("There are still shows showing at " + name);
            }
        }else{
            throw new ExistException("Venue " + name + " does not exists.");
        }
    }
    
    /*
     * modifies a venue for holding events
     */
    public void modifyVenue(String name, String address, String layout, Integer capacity, Double latitude, Double longtitude, boolean active) throws ExistException{
        venue = new Venue();
        Venue temp = new Venue();
        temp = em.find(Venue.class, name);
        if(temp != null){
            if (!(layout.matches("[\\w\\W]*.gif") || layout.matches("[\\w\\W]*.jpg")) || layout.matches("[\\w\\W]*.jpeg") || layout.matches("[\\w\\W]*.png") || layout.matches("[\\w\\W]*.bmp")) {
                layout = "picture/nolayout.gif";
            }
            venue.create(name, address, layout, capacity, latitude, longtitude, active);
            em.merge(venue);
            em.flush();
            System.out.println("modifyVenue(): Venue " + name + " modified.");
        }else{
            throw new ExistException("Venue " + name + " does not exists.");
        }
    }
    
    /*
     * view active venues
     */
    public List<Venue> viewActiveVenues(){
        List<Venue> allvenues = new ArrayList<Venue>();
        Query q = em.createQuery("SELECT v FROM Venue v");
        for(Object o: q.getResultList()){
            Venue temp = new Venue();
            temp = (Venue)o;
            if(temp.getActive() == true){
                allvenues.add(temp);
            }
        }
        return allvenues;
    }
    
    /*
     * view all venues
     */
    public List<Venue> viewVenues(){
        List<Venue> allvenues = new ArrayList<Venue>();
        Query q = em.createQuery("SELECT v FROM Venue v");
        for(Object o: q.getResultList()){
            Venue temp = new Venue();
            temp = (Venue)o;
            allvenues.add(temp);
        }
        return allvenues;
    }
    
    /*
     * removes a layout from venue
     */
    public void removeVenueLayout(String venueName) throws ExistException{
        venue = new Venue();
        venue = em.find(Venue.class, venueName);
        if(venue == null){
            throw new ExistException("Venue " + venueName + " does not exist.");
        }else{
            if(!venue.getLayout().equalsIgnoreCase("picture/nolayout.gif")){
                venue.setLayout("picture/nolayout.gif");
                em.merge(venue);
                em.flush();
                System.out.println("removeLayout(): " + venueName + " removed its layout.");
            }else{
                throw new ExistException("Venue " + venueName + " already has no layout.");
            }
        }
    }
            
    /*
     * Functionalities for StakeholdersPerformance
     */
    /*
     * add an event for a stakeholder // finished but incomplete
     */
    public void addEvent(Long stakeholderId, String eventName, String eventPoster, String description, String eventRating, 
            String eventLanguage, int eventDuration, String type, String genre, Calendar startDate, Calendar endDate) throws TimingException{
        event = new PerformanceItem();
        stakeholder = new StakeholderPerformance();
        stakeholder = em.find(StakeholderPerformance.class, stakeholderId);
        if(!(eventPoster.matches("[\\w\\W]*.gif") || eventPoster.matches("[\\w\\W]*.jpg")) || eventPoster.matches("[\\w\\W]*.jpeg") || eventPoster.matches("[\\w\\W]*.bmp") || eventPoster.matches("[\\w\\W]*.png")){
            eventPoster = "picture/noposter.gif";
        }
        if(startDate.after(endDate)){
            throw new TimingException("Dates not in Sequence");
        }
        event.create(eventName, eventPoster, description, eventRating, eventLanguage, eventDuration, type, genre, startDate, endDate);
        List<Showing> temp = new ArrayList<Showing>();
        event.setShow(temp);
        em.persist(event);
        stakeholder.getEvents().add(event);
        em.merge(stakeholder);
        em.flush();
        System.out.println("addEvent(): event " + eventName + " added.");
    }
    
    /*
     * removes an event // finished but incomplete
     */
    public void removeEvent(Long stakeholderId, Long id) throws OutstandingEventsException{
        event = new PerformanceItem();
        event = em.find(PerformanceItem.class, id);
        stakeholder = new StakeholderPerformance();
        stakeholder = em.find(StakeholderPerformance.class, stakeholderId);
        if(!(event.getShow().isEmpty())){
            throw new OutstandingEventsException(event.getName() + " still has outstanding shows.");
        }else{
            stakeholder.getEvents().remove(event);
            em.remove(event);
            em.flush();
            System.out.println("removeEvent(): event of " + id + " removed.");
        }
    }
    
    /*
     * modifies an event information // finished but incomplete
     */
    public void modifyEvent(Long stakeholderId, Long id, String name, String eventPoster, String description, String eventRating, 
            String eventLanguage, int eventDuration, String type, String genre, Calendar startDate, Calendar endDate) throws TimingException{
        event = new PerformanceItem();
        event = em.find(PerformanceItem.class, id);
        if(!(eventPoster.matches("[\\w\\W]*.gif") || eventPoster.matches("[\\w\\W]*.jpg")) || eventPoster.matches("[\\w\\W]*.jpeg") || eventPoster.matches("[\\w\\W]*.jpeg") || eventPoster.matches("[\\w\\W]*.png") || eventPoster.matches("[\\w\\W]*.bmp")){
            eventPoster = "picture/noposter.gif";
        }
        if(startDate.after(endDate)){
            throw new TimingException("Dates not in Sequence");
        }
        event.create(name, eventPoster, description, eventRating, eventLanguage, eventDuration, type, genre, startDate, endDate);
        em.merge(event);
        em.flush();
        System.out.println("modifyEvent(): Event " + id + " changed.");
    }
    
    /*
     * gets event name
     */
    public String getEventName(Long id){
        event = new PerformanceItem();
        event = em.find(PerformanceItem.class, id);
        return event.getName();
    }
    
    /*
     * gets event start date
     */
    public Calendar getEventStartDate(Long id){
        event = new PerformanceItem();
        event = em.find(PerformanceItem.class, id);
        return event.getStartDate();
    }
    
    /*
     * gets event end date
     */
    public Calendar getEventEndDate(Long id){
        event = new PerformanceItem();
        event = em.find(PerformanceItem.class, id);
        return event.getEndDate();
    }
    
    /*
     * gets a specific event
     */
    public List<PerformanceItem> getEvent(Long stakeholderId){
        stakeholder = new StakeholderPerformance();
        stakeholder = em.find(StakeholderPerformance.class, stakeholderId);
        return stakeholder.getEvents();
    }
    
    /*
     * removes a show layout
     */
    public void removeShowLayout(Long showId) throws ExistException{
        showing = new Showing();
        showing = em.find(Showing.class, showId);
        if(showing == null){
            throw new ExistException("Show #" + showId + " does not exist.");
        }else{
            if(!showing.getShowLayout().equalsIgnoreCase("picture/nolayout.gif")){
                showing.setShowLayout("picture/nolayout.gif");
                em.merge(showing);
                em.flush();
                System.out.println("removeShowLayout(): Show #" + showId + " removed its layout.");
            }else{
                throw new ExistException("Show already has no layout.");
            }
        }
    }
    
    /*public String getEventOrganiser(Long eventId) throws ExistException{
        stakeholder = new StakeholderPerformance();
        Query q = em.createQuery("SELECT s FROM StakeholderPerformance s");
        Object o = q.getSingleResult();
        stakeholder = (StakeholderPerformance)o;
        if(stakeholder == null){
            throw new ExistException("Stakeholder does not exist.");
        }else{
            return stakeholder.getName();
        }
    }*/
    
    public void addArea(String areaName, Seats[][] seats, double cost, Long showID) throws ExistException, OccupiedException{
        
        area = new Area();
        showing = new Showing();
        showing = em.find(Showing.class, showID);
        if(showing == null){
            throw new ExistException("Show doesn't exist."); 
        }
        for(Area a: showing.getAreas()){
            if(a.getAreaName().equalsIgnoreCase(areaName)){
                throw new ExistException(areaName + " already exist in show " + showID); 
            }
        }
        int current = 0;
        //boolean clash = false;
        for (int i = 0; i < showing.getAreas().size(); i++) {
            current += showing.getAreas().get(i).getCapacity();
            //Seats[][] tempshows = showing.getAreas().get(i).getSeats();
            // clash = checkSeats(tempshows, seats);
        }
        //if (!clash) {
        area.create(areaName, seats, cost);
        if ((current + area.getCapacity()) <= showing.getVenue().getCapacityCap()) {
            em.persist(area);
            showing.getAreas().add(area);
            System.out.println("addAreas(): " + area.getAreaName() + " added to show " + showing.getId() + " (" + dateTimeFormat.format(showing.getShowDate().getTime()) + ") show.");
        } else {
            throw new OccupiedException("Venue overloaded.");
        }
        /*} else {
        throw new ExistException("One of the areas already contains some/all of the seats.");
        }*/
    }
    
    /*
     * Check two seats arrays
     */
    private boolean checkSeats(Seats[][] current, Seats[][] newseats){
        boolean clash = false;
        List<Seats> tempCurrent = new ArrayList<Seats>();
        List<Seats> tempNew = new ArrayList<Seats>();
        for(int i = 0; i < current.length; i++){
            tempCurrent.addAll(Arrays.asList(current[i]));
        }
        for(int i = 0; i < newseats.length; i++){
            tempNew.addAll(Arrays.asList(newseats[i]));
        }
        for(int i = 0; i < tempCurrent.size(); i++){
            for(int j = 0; j < tempNew.size(); j++){
                if((tempCurrent.get(i).getColumn() == tempNew.get(j).getColumn()) && tempCurrent.get(i).getRow().equalsIgnoreCase(tempNew.get(j).getRow())){
                    clash = true;
                    break;
                }
            }
        }
        return clash;
    }
    
    /*
     * gets all available areas in a show
     */
    public List<Area> getAllAreas(Long id){
        showing = new Showing();
        showing = em.find(Showing.class, id);
        List<Area> output = new ArrayList<Area>();
        output = showing.getAreas();
        return output;
    }
    
    /*
     * removes an area
     */
    public void removeArea(Long id) throws ExistException, OutstandingEventsException{
        area = new Area();
        area = em.find(Area.class, id);
        showing = new Showing();
        Query q = em.createQuery("SELECT s FROM Showing s");
        for(Object o: q.getResultList()){
            Showing s = (Showing)o;
            List<Area> areas = new ArrayList<Area>();
            areas = s.getAreas();
            for(int i = 0; i < areas.size(); i++){
                if(areas.get(i).getId() == id){
                    showing = s;
                }
            }
        }
        if(area == null){
            throw new ExistException("Area of id" + id + " doesn't exist");
        }else{
            boolean nobooking = true;
            Seats[][] areaSeats = area.getSeats();
            for(int i = 0; i < areaSeats.length; i++){
                for(int j = 0; j < areaSeats[i].length; j++){
                    if(areaSeats[i][j].isReserved()){
                        nobooking = false;
                        break;
                    }
                }
            }
            if(nobooking){
                showing.getAreas().remove(area);
                em.remove(area);
                em.flush();
                System.out.println("removeArea(): area " + id + " removed.");
            }else{
                throw new OutstandingEventsException("There are bookings in this area: " + id);
            }
        }
    }
    
    /*
     * modifies an area
     */
    public void modifyArea(String areaName, Seats[][] seats, double cost, Long areaId) throws ExistException, OccupiedException{
        area = new Area();
        area = em.find(Area.class, areaId);
        showing = new Showing();
        Query q = em.createQuery("SELECT s FROM Showing s");
        for(Object o: q.getResultList()){
            Showing s = (Showing)o;
            for(Area a: s.getAreas()){
                if(a.getId().toString().equalsIgnoreCase(areaId.toString())){
                    showing = s;
                    break;
                }
            }
        }
        if (area == null) {
            throw new ExistException("Area with id " + areaId + " doesn't exist.");
        } else {
            int current = 0;
            // boolean clash = true;
            for (int i = 0; i < showing.getAreas().size(); i++) {
                if(!showing.getAreas().get(i).getAreaName().equalsIgnoreCase(areaName)){
                    current += showing.getAreas().get(i).getCapacity();
                }
                // Seats[][] tempshows = showing.getAreas().get(i).getSeats();
                // clash = checkSeats(tempshows, seats);
            }
            area.create(areaName, seats, cost);
            if ((current + area.getCapacity()) <= showing.getVenue().getCapacityCap()) {
                area.create(areaName, seats, cost);
                em.merge(area);
                em.flush();
                System.out.println("modifyAreas(): Area of id " + areaId + " changed.");
            } else {
                throw new OccupiedException("Venue overloaded.");
            }
            /*} else {
            throw new ExistException("One of the areas already contains some/all of the seats.");
            }*/
        }
    }
    
    /*
     * add show into an event
     */
    public void addShow(Long eventID, Calendar timing, String venueName, String showLayout) throws VenueConflictException, TimingException {
        event = new PerformanceItem();
        showing = new Showing();
        venue = new Venue();
        event = em.find(PerformanceItem.class, eventID);
        venue = em.find(Venue.class, venueName);
        if (venue == null) {
            throw new VenueConflictException("Venue " + venue + " does not exist.");
        }
        if(event.getStartDate().after(timing) || event.getEndDate().before(timing)){
            throw new TimingException("Timing clash.");
        }
        boolean clash = checkClash(venueName, timing, new Long(-1), event.getEventDuration());
        if(clash){
            throw new VenueConflictException("Venue and timing conflict.");
        }else{
            if (!(showLayout.matches("[\\w\\W]*.gif") || showLayout.matches("[\\w\\W]*.jpg")) || showLayout.matches("[\\w\\W]*.jpeg") || showLayout.matches("[\\w\\W]*.bmp") || showLayout.matches("[\\w\\W]*.png")) {
                showLayout = "picture/nolayout.gif";
            }
            showing.create(timing, showLayout);
            showing.setVenue(venue);
            em.persist(showing);
            event.getShow().add(showing);
            CalendarComparator compare = new CalendarComparator();
            Collections.sort(event.getShow(), compare);
            event.setShow(event.getShow());
            em.flush();
            System.out.println("AddShow(): " + dateFormat.format(timing.getTime()) + " added to " + event.getName() + ".");
        }
    }
    
    private boolean checkClash(String venueName, Calendar timing, Long showId, int leadTime){
        System.out.println(dateFormat.format(timing.getTime()) + " " + venueName + " " + showId);
        boolean output = false;
        Query q = em.createQuery("SELECT v FROM Showing v WHERE v.venue.name = :venueName");
        q.setParameter("venueName", venueName);
        for(Object o: q.getResultList()){
            Showing s = new Showing();
            s = (Showing) o;
            if (s.getId().compareTo(showId) != 0) {
                if (dateFormat.format(s.getShowDate().getTime()).equalsIgnoreCase(dateFormat.format(timing.getTime()))) {
                    Calendar showtime = s.getShowDate();
                    if (checkTiming(timing, showtime, leadTime)) {
                        output = true;
                        break;
                    }
                }
            }
        }
        return output;
    }
    
    private boolean checkTiming(Calendar timing, Calendar showtime, int leadtime){
        System.out.println("CheckTiming: timing is " + dateFormat.format(timing.getTime()));
        System.out.println("CheckTiming: checked against " + dateFormat.format(showtime.getTime()));
        System.out.println("CheckTiming: lead time is " + leadtime + " minutes.");
        Calendar minTime = Calendar.getInstance();
        Calendar maxTime = Calendar.getInstance();
        minTime.set(showtime.get(Calendar.YEAR), showtime.get(Calendar.MONTH), showtime.get(Calendar.DAY_OF_MONTH), showtime.get(Calendar.HOUR_OF_DAY), showtime.get(Calendar.MINUTE), showtime.get(Calendar.SECOND));
        maxTime.set(showtime.get(Calendar.YEAR), showtime.get(Calendar.MONTH), showtime.get(Calendar.DAY_OF_MONTH), showtime.get(Calendar.HOUR_OF_DAY), showtime.get(Calendar.MINUTE), showtime.get(Calendar.SECOND));
        minTime.add(Calendar.MINUTE, ((leadtime * -1) + 60));
        maxTime.add(Calendar.MINUTE, (leadtime + 60));
        System.out.println("Min: " + dateTimeFormat.format(minTime.getTime()));
        System.out.println("Max: " + dateTimeFormat.format(maxTime.getTime()));
        return (maxTime.after(timing) && minTime.before(timing));
    }
    
    /*
     * get all seats within a show
     */
    public Seats[][] getSeats(Long areaid){
        area = new Area();
        area = em.find(Area.class, areaid);
        Seats[][] output = area.getSeats();
        return output;
    }
    
    /*
     * remove show // finished
     */
    public void removeShow(Long showId, Long eventId) throws ExistException, OccupiedException{
        event = new PerformanceItem();
        showing = new Showing();
        event = em.find(PerformanceItem.class, eventId);
        if(event == null){
            throw new ExistException("Event " + eventId + " doesn't exist.");
        }
        for(Showing temp: event.getShow()){
            System.out.println(temp.getId() + ".compareTo(" + showId + ") = " + (temp.getId().compareTo(showId)));
            if(temp.getId().compareTo(showId) == 0){
                showing = temp;
                break;
            }
        }
        if(showing == null){
            throw new ExistException("Show " + showId + " doesn't exist.");
        }
        List<Area> areas = new ArrayList<Area>();
        areas = showing.getAreas();
        boolean removable = true;
        for(Area a: areas){
            for(int i = 0; i < a.getSeats().length; i++){
                for(int j = 0; j < a.getSeats()[i].length; j++){
                    if(a.getSeats()[i][j].isReserved()){
                        removable = false;
                    }
                }
            }
        }
        if(removable){
            event.getShow().remove(showing);
            for(Area a : showing.getAreas()){
                em.remove(a);
            }
            em.remove(showing);
            em.flush();
            System.out.println("Show " + showId + " removed.");
        }else{
            throw new OccupiedException("Show cannot be removed.");
        }
    }
    
    /*
     * modify show's timing // finished
     */
    public void modifyShow(Long showId, String venueName, String showLayout, Calendar timing) throws ExistException, VenueConflictException{
        showing = new Showing();
        showing = em.find(Showing.class, showId);
        venue = em.find(Venue.class, venueName);
        if(venue == null && showing == null){
            throw new ExistException("Show and Venue doesn't exist");
        }else if(showing == null){
            throw new ExistException("Show doesn't exist.");
        }else if(venue == null){
            throw new ExistException("Venue doesn't exist.");
        }
        int duration = 0;
        Query q = em.createQuery("SELECT p FROM PerformanceItem p");
        for(Object o: q.getResultList()){
            event = new PerformanceItem();
            event = (PerformanceItem)o;
            for(Showing s: event.getShow()){
                if(s.getId().toString().equalsIgnoreCase(showId.toString())){
                    duration = event.getEventDuration();
                }
            }
        }
        boolean clash = checkClash(venueName, timing, showId, duration);
        if(clash){
            throw new VenueConflictException("Venue and timing conflict.");
        }else{
            if (!(showLayout.matches("[\\w\\W]*.gif") || showLayout.matches("[\\w\\W]*.jpg")) || showLayout.matches("[\\w\\W]*.jpeg") || showLayout.matches("[\\w\\W]*.png") || showLayout.matches("[\\w\\W]*.bmp")) {
                showLayout = "picture/nolayout.gif";
            }
            showing.create(timing, showLayout);
            em.merge(showing);
            em.flush();
        }
        System.out.println("modifyShow(): show " + showId + " modified.");
    }
    
    /*
     * get specific timing
     */
    public Calendar getTiming(Long id){
        showing = new Showing();
        showing = em.find(Showing.class, id);
        return showing.getShowDate();
    }
    
    /*
     * add poster // finished
     */
    public void addPoster(String posterPath, Long eventId) throws ExistException{
        event = new PerformanceItem();
        event = em.find(PerformanceItem.class, eventId);
        if(event == null){
            throw new ExistException("Event of id " + eventId + " doesn't exist.");
        }
        event.setEventPoster(posterPath);
        em.merge(event);
        em.flush();
        System.out.println("addPoster(): Poster added to event: " + event.getName());
    }
    
    /*
     * remove poster // finished
     */
    public void removePoster(Long eventId) throws ExistException{
        event = new PerformanceItem();
        event = em.find(PerformanceItem.class, eventId);
        if(event == null){
            throw new ExistException("Event of id " + eventId + " doesn't exist.");
        }
        if(!event.getEventPoster().equalsIgnoreCase("picture/noposter.gif")){
            event.setEventPoster("picture/noposter.gif");
            em.merge(event);
            em.flush();
            System.out.println("removePoster(): Poster removed from event: " + event.getName());
        }else{
            throw new ExistException("Event already has no poster.");
        }
    }
    
    /*
     * modify poster // finished
     */
    public void modifyPoster(Long eventId, String eventPoster) throws ExistException{
        event = new PerformanceItem();
        event = em.find(PerformanceItem.class, eventId);
        if(event == null){
            throw new ExistException("Event of id " + eventId + " doesn't exist.");
        }
        event.setEventPoster(eventPoster);
        em.merge(event);
        em.flush();
        System.out.println("modifyPoster(): Poster changed for event: " + event.getName());
    }
    
    /*
     * Lists out all the shows that is showing
     */
    public List<PerformanceHelper> getNowShowing(){
        System.out.println("Now Showing: ");
        List<PerformanceHelper> events = new ArrayList<PerformanceHelper>();
        Query q = em.createQuery("SELECT e FROM PerformanceItem e");
        for(Object o: q.getResultList()){
            PerformanceHelper e = new PerformanceHelper();
            List<ShowHelper> shows = new ArrayList<ShowHelper>();
            event = new PerformanceItem();
            event = (PerformanceItem)o;
            if(((event.getStartDate()).before(Calendar.getInstance())) && (event.getEndDate().after(Calendar.getInstance())) && !(event.getShow().isEmpty())){
                e.setId(event.getId());
                e.setEventName(event.getName());
                e.setPoster(event.getEventPoster());
                e.setGenre(event.getEventGenre());
                e.setType(event.getEventType());
                e.setLanguage(event.getEventLanguage());
                e.setRating(event.getEventRating());
                e.setDescription(event.getDescription());
                e.setStartDate(dateFormat.format(event.getStartDate().getTime()));
                e.setEndDate(dateFormat.format(event.getEndDate().getTime()));
                e.setDescription(event.getDescription());
                e.setDuration(event.getEventDuration());
                for(Showing sh: event.getShow()){
                   ShowHelper s = new ShowHelper();
                   s.setId(sh.getId());
                   s.setTiming(dateTimeFormat.format(sh.getShowDate().getTime()));
                   s.setVenue(sh.getVenue().getName());
                   if(sh.getShowDate().after(Calendar.getInstance())){
                       shows.add(s);
                   }
                }
                e.setShowings(shows);
                System.out.println("For event " + e.getEventName() + ", there are " + e.getShowings().size() + " shows.");
                for(int i = 0; i < shows.size(); i++){
                    System.out.println("show #" + (i + 1) + " " + shows.get(i).getTiming() + "@" + shows.get(i).getVenue());
                }
                events.add(e);
            }
        }
        return events;
    }
    
    /*
     * Lists out all the shows that is coming soon
     */
    public List<PerformanceHelper> getComingSoon(){
        System.out.println("Coming Soon: ");
        List<PerformanceHelper> events = new ArrayList<PerformanceHelper>();
        Query q = em.createQuery("SELECT e FROM PerformanceItem e");
        for(Object o: q.getResultList()){
            PerformanceHelper e = new PerformanceHelper();
            List<ShowHelper> shows = new ArrayList<ShowHelper>();
            event = new PerformanceItem();
            event = (PerformanceItem)o;
            System.out.println(event.getName());
            if(event.getStartDate().after(Calendar.getInstance()) && !(event.getShow().isEmpty())){
                
                e.setId(event.getId());
                e.setEventName(event.getName());
                e.setPoster(event.getEventPoster());
                e.setGenre(event.getEventGenre());
                e.setType(event.getEventType());
                e.setLanguage(event.getEventLanguage());
                e.setRating(event.getEventRating());
                e.setDescription(event.getDescription());
                e.setStartDate(dateFormat.format(event.getStartDate().getTime()));
                e.setEndDate(dateFormat.format(event.getEndDate().getTime()));
                e.setDescription(event.getDescription());
                e.setDuration(event.getEventDuration());
                for(Showing sh: event.getShow()){
                    ShowHelper s = new ShowHelper();
                    s.setId(sh.getId());
                    s.setTiming(dateTimeFormat.format(sh.getShowDate().getTime()));
                    s.setVenue(sh.getVenue().getName());
                    //if (sh.getShowDate().after(Calendar.getInstance())) {
                        shows.add(s);
                    //}
                }
                e.setShowings(shows);
                System.out.println("For event " + e.getEventName() + ", there are " + e.getShowings().size() + " shows.");
                for(int i = 0; i < shows.size(); i++){
                    System.out.println("show #" + (i + 1) + " " + shows.get(i).getTiming() + "@" + shows.get(i).getVenue());
                }
                events.add(e);
            }
        }
        return events;
    }
    
    /*
     * checks if a show has areas to book
     */
    private boolean checkShowArea(List<Showing> s){
        boolean check = true;
        for(Showing sh: s){
            if(sh.getAreas().isEmpty()){
                check = false;
            }
        }
        return check;
    }
    
    /*
     * Lists out all the shows 
     */
    public List<PerformanceHelper> getShows(){
        List<PerformanceHelper> events = new ArrayList<PerformanceHelper>();
        Query q = em.createQuery("SELECT e FROM EventItem e");
        for(Object o: q.getResultList()){
            PerformanceHelper e = new PerformanceHelper();
            event = new PerformanceItem();
            event = (PerformanceItem)o;
            e.setEventName(event.getName());
            e.setPoster(event.getEventPoster());
            e.setDescription(event.getDescription());
            events.add(e);
        }
        return events;
    }
    
    /*
     * get all venues
     */
    public List<String> getAllActiveVenues(){
        System.out.println("Entered Here:");
        List<String> output = new ArrayList<String>();
        Query q = em.createQuery("SELECT v FROM Venue v");
        for(Object o: q.getResultList()){
            Venue temp = new Venue();
            temp = (Venue)o;
            if(temp.getActive()){
                output.add(temp.getName());
            }
        }
        return output;
    }
    
    /*
     * Functionalities for Users
     */
    /*
     * Gets all the seat in a specific area
     */
    public Seats[][] getSeatsinArea(String areaName, Long eventId, Calendar c) throws ShowNotFoundException{
        area = getArea(areaName, eventId, c);
        Seats[][] output = area.getSeats();
        return output;
    }
    
    /*
     * Check if a specific seat of a specific show is occupied
     */
    public boolean checkSeat(String areaName, String seatNum, Long eventId, Calendar c) throws ShowNotFoundException{
        Seats seat = new Seats();
        seat = getSeat(areaName, seatNum, eventId, c);
        System.out.println("Seat: " + seat.toString());
        System.out.println("Booking Status of " + seat.toString() + ": " + seat.isReserved());
        return seat.isReserved();
    }
    
    /*
     * book a seat for an event
     */
    public void bookSeat(String areaName, String seatNum, Long eventId, Calendar c) throws ShowNotFoundException{
        Seats seat = new Seats();
        PerformanceItem e = new PerformanceItem();
        e = em.find(PerformanceItem.class, eventId);
        area = getArea(areaName, eventId, c);
        seat = getSeat(areaName, seatNum, eventId, c);
        boolean current = seat.isReserved();
        System.out.println("For Seat " + seat.toString() + " of show " + e.getName());
        System.out.println("Currently booked: " + current);
        if(current != true){
            seat.setReserved(!current);
            System.out.println("Currently booked: " + seat.isReserved());
            int newCapacity = area.getCapacity() - 1;
            area.setCapacity(newCapacity);
        }
        System.out.println("Seat " + seatNum + " booked.");
    }
    
    /*
     * Other private methods
     */
    /*
     * private find a specific area
     */
    private Area getArea(String areaName, Long eventId, Calendar c) throws ShowNotFoundException{
        area = new Area();
        event = new PerformanceItem();
        showing = new Showing();
        event = em.find(PerformanceItem.class, eventId);
        for(int i = 0; i < event.getShow().size(); i++){
            if((event.getShow().get(i).getShowDate()).compareTo(c) == 0){
                showing = event.getShow().get(i);
            }
        } 
        if(showing == null){
            throw new ShowNotFoundException("No such show.");
        }else{
            List<Area> areas = new ArrayList<Area>();
            areas = showing.getAreas();
            for(Area a: areas){
                if(a.getAreaName().equalsIgnoreCase(areaName) ){
                    area = a;
                }
            }
        }
        return area;
    }
    
    /*
     * private - get a specific seat
     */
    private Seats getSeat(String areaName, String seatNum, Long eventId, Calendar c) throws ShowNotFoundException{
        Seats output = new Seats();
        area = new Area();
        area = getArea(areaName, eventId, c);
        Seats[][] s = area.getSeats();
        for(int i = 0; i < s.length; i++){
            for(int j = 0; j < s[i].length; j++){
                if(s[i][j].toString().equalsIgnoreCase(seatNum)){
                    output = s[i][j];
                }
            }
        }
        return output;
    }
    
    public List<ShowHelper> getSpecificShows(Long eventId){
        event = new PerformanceItem();
        event = em.find(PerformanceItem.class, eventId);
        List<ShowHelper> output = new ArrayList<ShowHelper>();
        List<Showing> shows = new ArrayList<Showing>();
        shows = event.getShow();
        CalendarComparator compare = new CalendarComparator();
        Collections.sort(shows, compare);
        for(Showing sh: shows){
            if(!(sh.getAreas().isEmpty()) && sh.getAreas() != null){
                ShowHelper s = new ShowHelper();
                s.setId(sh.getId());
                s.setTiming(dateDayFormat.format(sh.getShowDate().getTime()));
                s.setVenue(sh.getVenue().getName());
                output.add(s);
            }
        }
        System.out.println("correct!");
        return output;
    }
    
    public Boolean isAreaNull(Long areaId){
        Area output = new Area();
        output = em.find(Area.class, areaId);
        return (output == null);
    }
    
    public Showing getSelectedShow(Long id, String selectedSlot){
        event = new PerformanceItem();
        event = em.find(PerformanceItem.class, id);
        Showing output = new Showing();
        for(Showing s: event.getShow()){
            String check = dateDayFormat.format(s.getShowDate().getTime()) + " (" + s.getVenue().getName() + ")";
            System.out.println(selectedSlot);
            System.out.println(check);
            if(check.equalsIgnoreCase(selectedSlot)){
                output.setId(s.getId());
                output.setAreas(s.getAreas());
                output.setShowDate(s.getShowDate());
                output.setVenue(s.getVenue());
                output.setShowLayout(s.getShowLayout());
            }
        }
        System.out.println(output == null);
        return output;
    }
    
    /*
     * check for duplicate area name in a show.
     */
    public void checkAreaName(Long showId, String areaName) throws ExistException{
        showing = new Showing();
        showing = em.find(Showing.class, showId);
        for(Area a: showing.getAreas()){
            if(a.getAreaName().equalsIgnoreCase(areaName)){
                throw new ExistException(areaName + " already exist!");
            }
        }
    }
}
